Candid Systems Programming Site - ali.someone.net

Navigational Menu


Tutorials
  BMP Format
Projects
  FAST Viewer
Links
  VGA Programming

Introduction

Do you know how to read in a BMP file? Can you display a drawing made in MS-Paint using your own program? All this is covered in this tutorial. You will be able to display 256 colors bmp images on screen after reading it. Moving to PCX files is simply a matter of minutes, once you know BMP. The ground work done here is essential for VGA graphics programming. Please, send comments / suggestions / queries regarding this tutorial to [email protected]


What you need to know

This tutorial uses C language but the main part is in pseudocode, so you may translate it into any language. However, you should have read at least one book on programming to read any further. You should know how to open files, how to read a byte from a file, what are pixels and pointers, etc. Some basic knowledge of MODE13h and DOS interrupts will be useful but not necessary. If you don't understand anything, please email me.


Ground Work

We need a 320x200 bmp image, first of all. You can draw one yourself using your favourite paint program. Just keep in mind that the image dimensions should be width=320 and height=200 pixels. If you can't draw it (but why can't you draw it?) then download one such image.


Mode 13h

You have probably guessed we need to program in a graphics mode. Now the question is which one. We will program in mode13h which is a specific name for 320x200 resolution with 256 colors.


Why Mode 13h, anyway?

Because, its the fastest and easiest to program. Actually as we are going to display 256 color images, we need a graphical mode that supports that much colors. And as far as BGI graphics are concerned, they don't support 256 colors by default. Moreover BGI graphics are slow. So why not some other mode? I answered it already, its the easiest to program. By the way, there are other answers too: Other graphic modes are not standardized and I don't know anything else. =)


Ok, go ahead!

We use initgraph() in BGI drivers. When dealing with mode13h we have to use DOS interrupts to initialize grahpics mode. The following function sets up mode 13h:

/*-----------------------------------------
FUNCTION:	Sets graphics mode 13h
INPUT:		None
PROCESS:	Uses interrupts
OUTPUT:		None
------------------------------------------*/
void set_mcga (void)
   {
   union REGS regs;	// defined in dos.h
   regs.h.ah = 0;	// service number
   regs.h.al = 0x13;	// mode 13h
   int86 (0x10, & regs, & regs);	
   }
/*----------------------------------------*/

Similarly, after displaying the image we need to come back to text mode. The mode number of text mode is 0x3. When we have finished, we will call the following function:
/*----------------------------------------
FUNCTION:	Sets text mode
INPUT:		None
PROCESS:	Uses interrupts
OUTPUT:		None
-----------------------------------------*/
void set_text (void)
   {
   union REGS regs;	// defined in dos.h
   regs.h.ah = 0;	// to change mode
   regs.h.al = 0x03;	// text mode number
   int86 (0x10, & regs, & regs);
   }
/*---------------------------------------*/

Enough for now? No, not yet. We need to know how to plot a pixel in mode13h before start discussing the BMP format. The memory address for most of the graphics modes is 0xA0000000. We will use direct memory access. That is, we will define a pointer to this memory address. When we put a color value in this array (arrays and pointers are one and the same, remember), it will be automatically displayed on screen.
typedef unsigned char byte;
char far *video = (char far *) 0xA0000000;

/*----------------------------------------
FUNCTION:	Plots a pixel
INPUT:		Color and coordinates
PROCESS:	Uses *video
OUTPUT:		None
-----------------------------------------*/
void put_pixel (int x, int y, byte color)
   {
   if (y>=0 && y<200)	
      if (x>=0 && x<320)
	 video[y*320+x] = color;
   }
/*---------------------------------------*/

I forgot, there is one more thing - the palette. I quote a friend Greg Breland to explain what this is:
"Some graphics modes are what is called Palletized.�This means that they can only display a limited number of colors.�Mode 13h, for example, can only display 256 different colors on screen at once out of the 16 million that the human eye can see. However, you can choose which 256 colors.�Graphic cards store colors the way the monitor uses them, by RGB values (Red, Green, Blue).� By mixing different intensities of these three colors, any color in the rainbow can be made.�For 256 colors, there are 256 numbers each representing a color.�So when your image needs the color 25, the VGA card looks that color up in the index and sees that it is 64,0,0 or bright red (just for the sake of example). This is all handled by the graphics card.� The only thing you must do is set up these indices according to the 256 colors stored in the image."

So, how to change the color indices according to our image? We will define a function that writes to the ports related to the palette. Every device connected to a computer communicates via ports. When we need to change a particular color in the palette, we first tell the color number to the VGA card by writing it on port 0x3C8. Then, we write the RGB values on port 0x3C9.
/*----------------------------------------
FUNCTION:	Sets a color in palette
INPUT:		Color number and RGB
PROCESS:	Uses ports 0x3C8 and 0x3C9
OUTPUT:		None
-----------------------------------------*/
void set_palette (int c, byte r, byte g, byte b)
   {
   outportb (0x3C8, c);	// color number
   outportb (0x3C9, r);	// write red
   outportb (0x3C9, g); // green
   outportb (0x3C9, b); // and blue
   }
/*---------------------------------------*/


Round two, ready?

A bmp image file can be divided into three parts: a header (always 54 bytes), a palette (1024 bytes for a 256 color image) and the pixel values. At present as we are dealing with a 320x200 image, we can skip the header which contains information about the image.

Header, Skip it?

It's better to know the dimensions and other information about an image, before starting to display it. And its impossible to correctly display an image if you don't know its dimensions. If you don't want to skip it, here is the structure of the header:

struct bmpHeaderType
   {
   char magic1, magic2;	// identity 'BM'
   long fileSize;	// size of file in bytes
   long reserved;	// always 0
   long pixelOffset;	// offset of data
   long bmiSize;	// remaining header size
   long cols, rows;	// width and height
   int  nPlanes;	// number of color planes
   int  bitsPerPixel;
   long compression, cmpSize;	// compressed?
   long xScale, yScale;
   long colors, impColors;
   };
We use a simple fread() statement to read this structure after opening the bmp file with fopen in binary read mode.

Palette, Mr. Programmer!

How can we identify whether the bmp has 256 colors or 16 or 4? The answer lies in the bitsPerPixel and nPlanes variables. For 256 color images bitsPerPixel=8 and nPlanes=1. Now we have to read palette. Remember, there are 256 color numbers and each consists of 3 colors red, green and blue. So, you may be thinking that we need 3x256=768 bytes for the palette. That's true for a pcx image but in a bmp an extra reserved byte exists with these four. This makes up 256x4=1024 bytes. We will call the set_palette() function defined above for each color. Moreover, in a bmp the colors are stored as bgr and they have to be divided by four before calling this function (I don't know why).

for (x=0; x<256; x++)
  {
  b = fgetc (dataFile); b = b>>2;
  g = fgetc (dataFile); g = g>>2;
  r = fgetc (dataFile); r = r>>2;
  set_palette (x, r, g, b);
  fgetc (dataFile);
  }

The image

Once we have initialized the graphics mode and set up the palette, its very simple to display the image. We read in a color (stored in one byte) and plot it by calling put_pixel() defined above. We need to variables x and y for x and y axis respectively. The upper left coordinate of screen for all graphics modes is (0, 0) and the lower right one for mode13h is (319, 199). A single loop will do, but we have used two loops for clarity. And one more thing, a bmp image is stored last line first. That is why, we have to read the last line first and keep decrementing y until we have done the first line.

for (y=bmpHeader.rows; y>0; y--)
   for (x=0; x<bmpHeader.width; x++)
      put_pixel (x, y, fgetc(dataFile));

Download area

Following is the available stuff for you to download. I strongly recommend you to download at least the source file explained in this tutorial.

  • A 320x200 bmp image with 256 colors (12 KB)
  • Source file to display a bmp (2 KB)
  • Executable program to display a bmp (12 KB)
  • All of 'em (26 KB)

    What next?

    Now you can display a bmp with fixed width and height and will certainly want to display all sorts of bmp images. This not being told because its really very easy. If you stuck up some where, ask me. Keep only one hint in mind: The width of a bmp image is always a multiple of four. This means that if you read a header and come to know that the bmp image has a width of 65, its actually stored as 68 (a multiple of four).

    [Home] [C/C++] [Pascal] [Prolog] [HTML] [Java Script]

    Web Master: Muhammad Ali Shah. Created using Notepad. Please send your suggestions and comments to [email protected].